#include "public.h"
#include "log.h"
#include "event-internal.h"


struct event_select_s
{
	int	max_fd;
	fd_set	read_set;
	fd_set	write_set;
};


static void*    select_init( struct event_base_s *base, int maxevents );
static int      select_add( struct event_base_s *base, void *arg, struct event_s *ev );
static int      select_del( struct event_base_s *base, void *arg, struct event_s *ev );
static int      select_dispatch( struct event_base_s *base, void *arg, struct timeval *timeout );
static void     select_shutdown( struct event_base_s *base, void *arg );



struct	event_ops_s g_event_select = {
	.name		= "select",
	.init		= select_init,
	.add		= select_add,
	.del		= select_del,
	.dispatch	= select_dispatch,
	.shutdown	= select_shutdown,
};



static void*	select_init( struct event_base_s *base, int maxevents )
{
	struct event_select_s *sel;

	sel = (struct event_select_s *)malloc( sizeof(*sel) );
	if( NULL == sel )
		return NULL;

	sel->max_fd = 0;
	FD_ZERO( &sel->read_set );	
	FD_ZERO( &sel->write_set );	

	return sel;
}


static int	select_add( struct event_base_s *base, void *arg, struct event_s *ev )
{
	struct event_select_s *sel = (struct event_select_s*)arg;

	if( ev->events&EV_READ )
		FD_SET( ev->fd, &sel->read_set );
	if( ev->events&EV_WRITE )
		FD_SET( ev->fd, &sel->write_set );

	if( sel->max_fd != -1 && sel->max_fd < ev->fd )
		sel->max_fd = ev->fd;

	
	return 0;
}


static int	select_del( struct event_base_s *base, void *arg, struct event_s *ev )
{
	struct event_select_s *sel = (struct event_select_s*)arg;
	
	if( ev->fd == sel->max_fd )
		sel->max_fd = -1;

	if( ev->events&EV_READ )
		FD_CLR( ev->fd, &sel->read_set );
	if( ev->events&EV_WRITE )
		FD_CLR( ev->fd, &sel->write_set );

	return 0;
}



static int	select_dispatch( struct event_base_s *base, void *arg, struct timeval *timeout )
{
	int			nret, ii, nevents;
	fd_set			read_set, write_set;
	struct event_s		*ev;
	struct event_select_s	*sel = (struct event_select_s*)arg;

	if( sel->max_fd == -1 )
	{
		for( ii =0; ; ii++ )
		{
			ev = event_base_array_get( base, ii );
			if( NULL == ev )
				break;
			if( sel->max_fd < ev->fd )
				sel->max_fd = ev->fd;
		}
	}

	read_set	= sel->read_set;
	write_set	= sel->write_set;
	nret = select( sel->max_fd + 1, &read_set, &write_set, NULL, timeout );
	if( -1 == nret )
	{
		log_error( base->log, "select() error %d", nret );
		return -1;
	}
	else if( 0 == nret )
	{
		log_debug( base->log, "select() timeout" );
		return 0;
	}
	
	log_debug( base->log, "select() get event %d", nret );

	for( ii = nevents = 0; ; ii++ )
	{
		ev = event_base_array_get( base, ii );
		if( NULL == ev ) 
		{
			if( nevents != nret )
				log_debug( base->log, "found events %d active %d", nret, nevents );
			break;
		}

		if( (ev->events&EV_READ) && FD_ISSET( ev->fd, &sel->read_set ) )
		{
			event_base_active( base, ev, EV_READ );
			nevents++;
		}
		
		if( (ev->events&EV_WRITE) && FD_ISSET( ev->fd, &sel->write_set ) )
		{
			event_base_active( base, ev, EV_WRITE );
			nevents++;
		}
			
		if( nevents >= nret ) break;
	}
	
	return 0;
}



static void	select_shutdown( struct event_base_s *base, void *arg )
{
	struct event_select_s	*sel = (struct event_select_s*)arg;

	free( sel );
}


	



